Sequelize로 CRUD 수행하기 (Request Method)

✒️ 2025-05-26 15:07 내용 수정


사전 설정

// 1. require
const express = require('express');
const app = express();

//db
const db = require('./models/index');
//const employee = require('./models/employee'); // 이 부분은 주석해도 무관
const { Employee } = db;

// 2. use, set
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/public/js'));
app.set('view engine', 'ejs');
app.use(express.json());
app.use(express.urlencoded({'extended':'true'}));

// 3. listen
// rest client를 사용했을 때 8080 포트로 연결이 안되서 변경함
app.listen('3000', () => {
    console.log('접속 http://localhost:3000'); 
});

// 4. routing
app.get('/', (request, response) => {
    response.render('main.ejs');
});
*{margin: 0; padding:0; box-sizing:border-box;}
ul, ol, li{list-style: none;}
a{text-decoration: none;}

/* section영역 */
.sec{width: 100%;padding: 100px 0;}
.sec .title{margin: 0 0 40px; font: bold 24px 'inherit'; text-align: center;}

.employee table{width: 100%; border-spacing: 0; border-collapse: collapse; border-bottom: 1px solid #ddd;}
.employee th,
.employee td{border-top: 1px solid #ddd; padding: 5px 0;}
.employee th{background-color: #fdd;}
.employee table td.email{text-align: left;}

.employee tr:nth-of-type(2n-1){background-color: #f9f9f9;}

@media screen and (max-width: 768px) {
  
  .employee th{display: none;}
  .employee td{display:block;}
  .employee td:first-of-type{
    display: table-cell; 
    background-color: #f9f9f9; 
  }

  .employee td{text-align: left;}
  .employee td.id{text-align: center; background-color: #eee;}
  .employee td.name{font-weight: bold; text-align: center; background-color: #eee;}

  .employee td::before{ display: inline-block; padding: 0 20px; width: 100px;} 
  .employee td.team::before{content: 'team';}
  .employee td.position::before{content: 'position'; }
  .employee td.email::before{content: 'email';}
  .employee td.tel::before{content: 'phone';}
  .employee td.birthday::before{content: 'birthday';}
}

1. 데이터 조회

  1. server.js 파일에서 이전에 작성했던 내용과 동일하지만 코드를 좀 더 깔끔하게 정리하고, 이번엔 try-catch문을 사용해서 에러 처리도 진행한다.
    • 요청 메소드를 작성하고, callback 함수를 작성하는데, 이번엔 callback 함수를 함수 표현식이 아닌 함수 선언으로 만들어 사용한다.
    • Resources/Java/예외 처리/예외 처리 참고.
// 1. 데이터 조회
app.get("/employee", employeeSearchQuery);
app.get("/employee/:id", employeeSearchParam);


// 데이터 조회 query string
async function employeeSearchQuery(request, response) {
    const {id, team, position} = request.query; // query string의 key에 저장된 value들을 가져옴
    let employee = null; // employee 변수를 null로 설정

    try { // 각 query string의 key의 value 상태에 따라 MySQL에 조회 조건을 다르게 설정
        if (id) {
            employee = await Employee.findAll({where:{id}});
        } else if (team) {
            employee = await Employee.findAll({where:{team}});
        } else if (position) {
            employee = await Employee.findAll({where:{position}});
        } else if (team && position) {
            employee = await Employee.findAll({where:{team, position}});
        } else {
            employee = await Employee.findAll();;
        }
        // 조회한 결과를 페이지에 전송
        response.render('employee.ejs', {employee});
    } catch (error) { // 에러가 발생하면 콘솔에 출력하고, status 적용과 메시지를 전송
        console.log(error);
        response.status(500).send('서버 에러 발생');
    }
}

// 데이터 조회 param
// parameter를 받는 경우의 메소드
async function employeeSearchParam(request, response) {
    const { id } = request.params; 

    try { // 전달 받은 id값으로 해당 직원을 조회
        const employee = await Employee.findOne({where:{id}});
        console.log({id});

        if (employee) { // 직원이 존재하면 해당 직원 상세보기 페이지에 데이터를 전송
            response.render('employee-detail.ejs', {employee});
        } else { // 직원이 조회가 안된다면 존재하지 않다는 메시지 전송
            response.send('존재하지 않음');
        }
    } catch (error) { // 에러 처리
        console.log(error);
        response.status(500).send('서버 에러 발생');
    }
}
  1. 이전과 동일하게 전체 직원을 조회할 ejs 파일과 버튼 액션을 처리할 js 파일을 만든다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <!-- 파일 연결 시 /는 밴다 -->
    <link href="main.css" type="text/css" rel="stylesheet">
</head>
<body>
    <section class="sec employee">
        <div class="container">
            <h2 class="title">Employee List</h2>
            <div class="row">
                <div class="col">
                    <div class="search">
                        <div class="option-box">
                            <label for="group">choose team or position : </label>
                            <select id="group">
                                <option value="total">total</option>
                                <optgroup label="team">
                                    <option value="engineering" class="team-option">engineering</option>
                                    <option value="marketing" class="team-option">marketing</option>
                                    <option value="sales" class="team-option">sales</option>
                                </optgroup>
                                <optgroup label="position">
                                    <option value="Server Developer" class="position-option">Server Developer</option>
                                    <option value="Android Developer" class="position-option">Android Developer</option>
                                    <option value="Web Frontend Developer" class="position-option">Web Frontend Developer</option>
                                    <option value="Marketing Manager" class="position-option">Marketing Manager</option>
                                    <option value="Marketing Staff" class="position-option">Marketing Staff</option>
                                    <option value="Sales Manager" class="position-option">Sales Manager</option>
                                    <option value="Sales Staff" class="position-option">Sales Staff</option>
                                </optgroup>
                            </select>
                        </div>
                        <div class="search-box">
                            <input type="text" id="search" placeholder="input employee id">
                            <button id="send-btn">send</button>
                        </div>
                    </div>
                    <a href="/add-page" >회원 추가</a>
                    <div class="info-box">
                        <table>
                            <tr>
                                <th>id</th>
                                <th>이름</th>
                                <th>부서</th>
                                <th>직종</th>
                                <th>이메일</th>
                                <th>전화번호</th>
                                <th>생일</th>
                            </tr>
                            <tr>
                                <% for(let i = 0; i < employee.length; i++) { 
                                    %>
                                    <tr>
                                        <td class="id"> <%= employee[i].id %> </td>
                                        <td class="name">
                                            <a href="/employee/<%= employee[i].id %>"><%= employee[i].name %></a>
                                        </td>
                                        <td class="team"> <%= employee[i].team %> </td>
                                        <td class="position"> <%= employee[i].position %> </td>
                                        <td class="email"> <%= employee[i].emailAddress %> </td>
                                        <td class="tel"> <%= employee[i].phoneNumber %> </td>
                                        <!-- 날짜 처리를 toLocaleString() 메소드로 더 간단하게 처리 -->
                                        <td class="birthday"> <%= employee[i].birthday.toLocaleString("ko-kr",{dateStyle:'long'}) %> </td>
                                    </tr>
                                <% } %>
                            </tr>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script type="text/javascript" src="action.js" ></script> <!-- src로 주소 설정 시 /는 뺀다 -->
</body>
</html>
// 직원 정보 페이지의 기본 url
let employee_url = "http://localhost:3000/employee";

// 드롭다운 메뉴의 요소들
let group = document.getElementById("group");
let teamList = document.getElementsByClassName("team-option");
let positionList = document.getElementsByClassName("position-option");
let default_option = document.querySelector(".search-info");

// 검색창의 요소들
let search_box = document.querySelector("#search");
let send_btn = document.querySelector("#send-btn");

// 부서 이름과 직종 이름을 저장할 배열
let teamName = [];
let positionName = [];

// 현재 query string의 value에서 공백 특수 문자를 공백으로 변환
let currentQuery = window.location.search.split("=")[1];
if (currentQuery) {
  if (currentQuery.includes("%20")) {
    currentQuery = currentQuery.replace("%20", " ");
  }
}

// option 태그들의 페이지별 selected Attribute 설정
// 기본 option 태그를 선택 상태로 만들어 전체 검색을 위한 동작도 가능하게 수정
if (window.location.href === employee_url) {
  default_option.setAttribute("selected", true);
} else {
  default_option.setAttribute("selected", false);
}

// option 태그의 value를 가져와 부서 이름 배열에 이름을 저장
for (let i = 0; i < teamList.length; i++) {
  teamName[i] = teamList[i].value;
  if (currentQuery == teamList[i].value) {
    teamList[i].setAttribute("selected", true); // 현재의 query string의 value와 일치하는 option 태그를 selected 상태로 설정
  }
}

// option 태그의 value를 가져와 직종 이름 배열에 이름을 저장
for (let i = 0; i < positionList.length; i++) {
  positionName[i] = positionList[i].value;
  if (currentQuery == positionList[i].value) {
    positionList[i].setAttribute("selected", true); // 현재의 query string의 value와 일치하는 option 태그를 selected 상태로 설정
  }
}

// event listener
group.addEventListener("change", function () { // select 태그는 change일 때 설정이 잘 적용되었음
  let flag_team = false; // 부서 이름인지 확인하는 flag
  let flag_position = false; // 직종 이름인지 확인하는 flag

  // 부서 이름인지 확인
  teamName.forEach((el) => {
    if (group.value == el) {
      flag_team = true;
    }
  });

  // 직종 이름인지 확인
  positionName.forEach((el) => {
    if (group.value == el) {
      flag_position = true;
    }
  });

  // select 태그에서 선택한 내용이 부서인지, 직종인지, 전체인지 확인
  if (flag_team) {
    location.href = employee_url + "?team=" + group.value; // query string에 부서 정보를 전달
    flag_team = false; // flag를 다시 false로 변환
  } else if (flag_position) {
    location.href = employee_url + "?position=" + group.value; // query string에 직종 정보를 전달
    flag_position = false;
  } else if (group.value == "total") { // query string을 제거하고 전체 조회를 수행
    location.href = employee_url;
  }
});

// 직원 번호 입력창 설정
send_btn.addEventListener("click", function () {
  location.href = employee_url + "?id=" + search_box.value; // 버튼 클릭 시 직원 id를 query string으로 전달
});
  1. 특정 직원의 정보를 상세히 보거나 직원의 정보를 수정, 삭제할 수 있는 페이지를 만든다.
    • button 태그에 data-customName="value"을 추가해 원하는 value를 dataset.customName attribute로 설정한다.
    • 버튼을 눌렀을 때 이 값이 url을 통해 같이 전달될 수 있도록 설정한다.
    • new Date('날짜').toLocaleString("ko-kr",{dateStyle:'long'})를 사용하면 xxxx년 x월 x일로 date가 표기된다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <link href="/main.css" type="text/css" rel="stylesheet">
</head>
<body>
    <section class="sec employee">
        <div class="container">
            <h2 class="title">Employee View</h2>
            <div class="row">
                <div class="col">
                        <table>
                            <tr>
                                <th class="col">id</th>
                                <td class="id"><%= employee.id %></td>
                            </tr>
                            <tr>
                                <th class="col">이름</th>
                                <td class="name"><%= employee.name %></td>
                            </tr>
                            <tr>
                                <th class="col">부서</th>
                                <td class="team"><%= employee.team %></td>
                            </tr>
                            <tr>
                                <th class="col">직종</th>
                                <td class="position"><%= employee.position %></td>
                            </tr>
                            <tr>
                                <th class="col">이메일</th>
                                <td class="email"><%= employee.emailAddress %></td>
                            </tr>
                            <tr>
                                <th class="col">전화번호</th>
                                <td class="tel"><%= employee.phoneNumber %></td>
                            </tr>
                            <tr>
                                <th class="col">생일</th>
                                <td class="birthday"><%= employee.birthday.toLocaleString("ko-kr",{dateStyle:'long'}) %></td>
                            </tr>
                        </table>
                        <div class="btn-wrap">
                            <!-- custom data attribute : data-customName="value" -->
                            <button class="modify" type="button" data-id="<%= employee.id %>">수정</button>
                            <button class="del" type="button" data-id="<%= employee.id %>">삭제</button>
                            <button class="cancel" type="button">취소</button>
                        </div>
                </div>
            </div>
        </div>
    </section>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

    <script type="text/javascript" src="/main.js"></script>
</body>
</html>
// 삭제 버튼 - 4. 데이터 삭제 참고
let del = document.querySelector(".del");

if (del) { // 문서에 del이 존재할때만
    del.addEventListener('click', function (e) {
        // 버튼을 눌렀을 때 버튼에 설정한 사용자 지정 data attribute를 가져온다.
        // data-customName="value"
        let id = e.target.dataset.id;

        fetch('/delete/'+id, {method : 'delete'})
        .then((response) => {response.text()})
        .then((result) => {
            console.log(result);
        })
        .catch((error) => {
            console.log(error);
        });
    });
}

// 수정 페이지 이동 버튼 - 3. 데이터 수정 참고
let modify = document.querySelector(".modify");

if (modify) { // 문서에 modify가 존재할 때만
    modify.addEventListener('click', function (e) {
        // 버튼을 눌렀을 때 버튼에 설정한 사용자 지정 data attribute를 가져온다.
        // data-customName="value"
        let id = e.target.dataset.id;
        window.location.href = "/edit-page/"+id;
    });
    
}

// 취소 버튼
let cancel = document.querySelector(".cancel");

if (cancel) {
    cancel.addEventListener('click', function () {
        window.location.href = "/employee"; // 취소 버튼을 누르면 직원 조회 페이지로 이동
    })
}

node_crud_request 1.png


2. 데이터 추가

  1. server.js 파일에 데이터 추가를 위한 요청 메소드와 callback 함수를 작성한다.
    • 이번엔 데이터 추가를 위한 페이지가 필요하므로 페이지 이동용 get 메소드도 추가한다.
// 2. 데이터 추가
app.get("/add-page", employeeAddPage); // 추가 페이지 이동
app.post("/add", employeeAdd);


// 데이터 추가 페이지
function employeeAddPage(request, response) { // 페이지 이동 처리만 해준다.
    try {
        response.render('employee-add.ejs');
    } catch (error) {}
}

// 데이터 추가
async function employeeAdd(request, response) {
    const newEmpolyee = request.body; // html의 form 태그에서 데이터를 전송하면 request.body에 저장됨
    console.log(newEmpolyee);
    try {
        const employee = await Employee.create(newEmpolyee); // build+save를 하거나 create를 사용해서 추가
        response.redirect('/employee'); // 직원 추가가 완료되면 직원 조회 mapping을 통해 이동
    } catch (error) { // 에러처리
        console.log(error);
        response.status(500).send('서버 에러 발생');
    }
}
  1. 데이터 추가를 위한 ejs 파일을 만든다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <!-- url에 파라미터를 전달 받을 때 에러가 나던 employee.ejs와 다르게 여기선 주소에 /를 써도 css가 적용된다 -->
    <link href="/main.css" type="text/css" rel="stylesheet">
</head>
<body>
    <section class="sec employee">
        <div class="container">
            <h2 class="title">Add Employee</h2>
            <div class="row">
                <div class="col">
	                <!-- form 태그에 action과 method를 지정한다 -->
	                <!-- action="/add"로 app.post("/add", callback)이 호출됨 -->
                    <form action="/add" method="post">
                        <table>
                            <tr>
                                <th class="col"><label for="addName">이름</label></th>
                                <td><input type="text" id="addName" name="name"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addTeam">부서</label></th>
                                <td><input type="text" id="addTeam" name="team"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addPosition"></label>직종</label></th>
                                <td><input type="text" id="addPosition" name="position"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addEmail">이메일</label></th>
                                <td><input type="text" id="addEmail" name="emailAddress"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addPhone">전화번호</label></th>
                                <td><input type="text" id="addPhone" name="phoneNumber"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addBirthday"></label>생일</label></th>
                                <td><input type="date" id="addBirthday" name="birthday"></td>
                            </tr>
                        </table>
                    </form>
                    <div class="btn-wrap">
	                    <!-- submit을 사용하면 form에 지정된 action으로 바로 동작함 -->
                        <button class="add-btn" type="sumbit">확인</button>
                        <button class="cancel" type="button">취소</button>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

	<!-- url에 파라미터를 안 받는 이 페이지에선 주소에 /를 써도 js가 적용된다 -->
    <script type="text/javascript" src="/main.js"></script>
</body>
</html>
// 추가 버튼
let add_btn = document.querySelector(".add-btn");

if (add_btn) {
    add_btn.addEventListener('click', function (e) {
		// 만약 add_btn이 submit이 아닌 button 타입이라면 이 부분이 필요하다.
		// submit 타입이라면 form 태그에 지정한 action이 수행되므로 fetch가 없어도 바로 데이터가 전송된다.
        fetch('/add', {method : 'post'}) // fetch로 데이터 전송
        .then((response) => {response.text()})
        .then((result) => { // 데이터가 잘 전송되면 콘솔에 결과를 작성
            console.log(result);
        })
        .catch((error) => {
            console.log(error);
        });
    });
}

// 취소 버튼
let cancel = document.querySelector(".cancel");

if (cancel) {
    cancel.addEventListener('click', function () {
        window.location.href = "/employee"; // 취소 버튼을 누르면 직원 조회 페이지로 이동
    })
}

node_crud_request 3.png


3. 데이터 수정

  1. server.js 파일에서 데이터 수정을 위한 요청 메소드와 callback 함수를 작성한다.
    • 수정은 두 가지 방법 중 하나를 선택해서 함수를 작성한다.
// 3. 데이터 수정
// 이번엔 parameter(path variable)을 둘 다 받는다
app.get("/edit-page/:id", empolyeeModifyPage); // 수정 페이지 이동
app.post("/edit/:id", empolyeeModify);


// 데이터 수정 페이지
async function empolyeeModifyPage(request, response) {
    const {id} = request.params; // url에 포함된 parameter를 저장
    const employee = await Employee.findOne({where:{id}}); // id로 직원을 조회

    if(employee) { // 직원이 존재하는지 확인
        try {
            response.render('employee-edit.ejs', {employee});
        } catch (error) { // 조회 과정에 오류가 생기면 에러 처리
            response.status(404).send({message: '회원 조회 실패'});
        }
    } // 직원이 없다면 수정 과정이 없음
}

// 데이터 수정
// 직원을 조회하고 객체의 property를 새로 지정한 후 save하는 방법
async function empolyeeModify(request, response) {
    const {id} = request.params; // 여기에도 id가 파라미터로 같이 넘어왔기에 저장한다
    console.log(id)
    
    const newInfo = request.body; // 수정 페이지의 form 태그에서 데이터를 전송하면 request.body에 저장됨
    const employee = await Employee.findOne({where:{id}}); // 수정을 위해 id로 직원을 조회함

    if (employee) { // 직원이 있다면
        try {
            Object.keys(newInfo).forEach((prop) => { 
                // 조회한 직원 객체의 property를 가져와서 수정할 property로 저장
                employee[prop] = newInfo[prop];
            });

            await employee.save(); // 수정한 내용을 저장
        
            response.redirect('/employee'); // 수정이 완료되면 직원 조회 페이지로 이동
        } catch (error) { // 에러 처리
            console.log(error);
            response.status(500).send('서버 에러 발생');
        }
    }
}

// 데이터 수정
// 직원을 조회하고 객체의 property를 새로 지정한 후 save하는 방법
async function empolyeeModify(request, response) {
    const {id} = request.params; // 여기에도 id가 파라미터로 같이 넘어왔기에 저장한다
    console.log(id);
    
    const newInfo = request.body; // 수정 페이지의 form 태그에서 데이터를 전송하면 request.body에 저장됨

    try {
	    let result = await Employee.update(newInfo, {where:{id}}); // 특정 id의 데이터를 조회해서 새 정보로 업데이트
        
        response.redirect('/employee'); // 수정이 완료되면 직원 조회 페이지로 이동
    } catch (error) { // 에러 처리
        console.log(error);
        response.status(500).send('서버 에러 발생');
    }
}
  1. 데이터 수정을 위한 ejs 파일을 만든다.
    • put을 작성할 때 주의사항 : HTML의 form 태그의 메소드에는 get과 post 밖에 없기 때문에 put을 쓰면 query string에 내용이 전부 포함된 GET 메소드로 request가 전송되고, 제대로 수정되지 않는다.
    • 수정 테스트만 한다면 <form method="put">이 아닌 <form method="post">로 작성하고, app.put()fetch("", {method : "put"})으로 작성해야 한다.
    • 관련 내용을 더 찾아봤을 때는 RestFul API 구현을 위해 GET, POST, PUT, DELETE 메소드를 따로 구분해서 작성하며, 개발하려는 상황에 맞춰 필요한 메소드를 구현한다고 한다.
    • POST로 PUT을 구현할 수 있음에도 다른 라이브러리를 이용해 메소드 오버라이딩을 통해 PUT과 DELETE를 구현하는 방법이 있다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <link href="/main.css" type="text/css" rel="stylesheet">
</head>
<body>
    <section class="sec employee">
        <div class="container">
            <h2 class="title">Edit Employee</h2>
            <div class="row">
                <div class="col">
                    <!-- html에는 get과 post만 있기 때문에 둘 중 하나만 쓰고 fetch와 app에서 put을 지정해야 함-->
                    <!-- form의 action에서도 id를 url의 파라미터로 넘겨줘야 하므로 표현식을 써서 넘김 -->
                    <form action="/edit/<%= employee.id %>" method="post">
                        <table>
	                        <!-- 수정페이지에선 해당 직원의 원래 정보가 뜨도록 표시 -->
                            <tr>
                                <th class="col"><label for="addName">이름</label></th>
                                <td><input type="text" id="addName" name="name" value="<%= employee.name %>"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addTeam">부서</label></th>
                                <td><input type="text" id="addTeam" name="team" value="<%= employee.team %>"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addPosition"></label>직종</label></th>
                                <td><input type="text" id="addPosition" name="position" value="<%= employee.position %>"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addEmail">이메일</label></th>
                                <td><input type="text" id="addEmail" name="emailAddress" value="<%= employee.emailAddress %>"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addPhone">전화번호</label></th>
                                <td><input type="text" id="addPhone" name="phoneNumber" value="<%= employee.phoneNumber %>"></td>
                            </tr>
                            <tr>
                                <th class="col"><label for="addBirthday"></label>생일</label></th>
                                <!-- MySQL의 Date 형식은 Javascript의 datepicker 형식과 맞지 않기에 수정 필요 -->
                                <% let birthday = new Date(employee.birthday).toISOString().split('T')[0]; %>
                                <td><input type="date" id="addBirthday" name="birthday" value="<%= birthday %>"></td>
                            </tr>
                        </table>
                    </form>
                    <div class="btn-wrap">
                        <button class="put-btn" type="submit" data-id="<%= employee.id %>" >확인</button>
                        <button class="cancel" type="button">취소</button>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

    <script type="text/javascript" src="/main.js"></script>
</body>
</html>
// 수정 버튼
let put_btn = document.querySelector(".put-btn");

if (put_btn) {
    put_btn.addEventListener('click', function(e) {
        // 이미 form 태그에 action을 지정한 상태로 button 태그의 타입이 submit이라면 fetch를 생략해도 된다.
        // fetch로 put 메소드 방식으로 app.put("/edit/:id", callback)을 호출한다.
        let id = e.target.dataset.id; 
        //console.log(id);

        fetch("/edit/"+id, {method : "put"}) 
        .then((response) => {
            response.text()
        })
        .then((result) => {
            console.log(result)
        })
        .catch((error) => {
            console.log(error)
        })
    });
}

// 취소 버튼
let cancel = document.querySelector(".cancel");

if (cancel) {
    cancel.addEventListener('click', function () {
        window.location.href = "/employee"; // 취소 버튼을 누르면 직원 조회 페이지로 이동
    })
}
let put_btn = document.querySelector(".put-btn");

// form 태그 내의 정보들을 가져온다.
let addName = document.getElementById('addName');
let addTeam  =  document.getElementById('addTeam');
let addPosition =  document.getElementById('addPosition');
let addEmail =  document.getElementById('addEmail');
let addPhone =  document.getElementById('addPhone');
let addBirthday =  document.getElementById('addBirthday');

put_btn.addEventListener('click', function(e){
    // 이미 form 태그에 action을 지정한 상태로 button 태그의 타입이 submit이라면 fetch를 생략해도 된다.
    // fetch로 put 메소드 방식으로 app.put("/edit/:id", callback)을 호출한다.

	// 버튼을 눌렀을 때 버튼에 설정한 사용자 지정 data attribute를 가져온다.
    // data-customName="value"
    let id = e.currentTarget.dataset.id;

	// newInfo 객체에 가져온 정보들을 저장하기
	let newInfo ={
	    id, // db에서 auto_increment가 적용되었기에 값을 넣지 않음
	    name : addName.value,
	    team : addTeam.value,
	    position : addPosition.value,
	    emailAddress : addEmail.value,
	    phoneNumber : addPhone.value,
	    birthday : addBirthday.value
	  }

	fetch('/edit/'+id, {
	    method : 'PUT', // 수정을 위해 put으로 요청
	    headers: { 'Content-Type': 'application/json'},  // JSON 형식으로 데이터를 전송
	    body: JSON.stringify(newInfo) // request.body의 내용을 json(string) 형식으로 변환
	  }) 
	  .then((response) => {
		  response.text()
		})
	  .then((result) => {
	    console.log(result);
	    window.location.href="/employee"; // 요청 성공 시 직원 조회 페이지로 이동
	  })
	  .catch((error) => { 
	    console.log(error);
	})
}); 

node_crud_request 2.png


4. 데이터 삭제

  1. server.js 파일에서 데이터 삭제를 위한 요청 메소드와 callback 함수를 작성한다.
    • 삭제 버튼은 직원 상세보기 페이지에 있으며, 삭제 페이지가 따로 필요 없다.
// 4. 데이터 삭제
app.delete("/delete/:id", employeeDelete); // 여기도 id를 파라미터로 받는다.

// 데이터 삭제
async function employeeDelete(request, response) {
    const {id} = request.params; // url로 전달된 id를 저장

    console.log(id);
    try {
        const result = await Employee.destroy({where:{id}}); // 해당 id의 직원 정보를 삭제한다.
        response.redirect('/employee'); // 제거가 완료되면 직원 조회 페이지로 이동해야 하는데 이동이 잘 안된다
    } catch (error) { // 에러가 발생하면 처리
        console.log(error);
        response.status(500).send('서버 에러 발생');
    }
}
  1. 삭제 동작을 위한 버튼 액션 지정용 Javascript 파일을 작성한다.
// 삭제 버튼
let del = document.querySelector(".del");

if (del) {
    del.addEventListener('click', function (e) {
        // 버튼을 눌렀을 때 버튼에 설정한 사용자 지정 data attribute를 가져온다.
        // data-customName="value"
        let id = e.target.dataset.id; 

		// fetch로 delete 메소드 방식으로 app.delete("/delete:/id", callback)를 호출한다.
        fetch('/delete/'+id, {method : 'delete'}) 
        .then((response) => {response.text()})
        .then((result) => {
            console.log(result);
        })
        .catch((error) => {
            console.log(error);
        });
    });
}

node_crud_request 4.png